#include "AEE.h"
#include "fsmodel.h"
#include "VectorModel.h"
#include "AEEFile.h"
#include "wutil.h"

typedef struct FSModel
{
	VectorModel base;
	IShell *shell;
	IFileMgr *fmgr;
	AEECallback traverseCB;
	AEECallback imageCB;
	IVectorModel *self;
	IVectorModel *dirsToLoad;
	IVectorModel *imagesToLoad;
	uint32 flags;
} FSModel ;

typedef struct
{
	FSModel *me;
	NodeFile *node;
}  ImageLoadData ;

static void InitEnumDir(FSModel *me);
static void LoadImage(void *data);

static void DestroyNode(void *data)
{
	if(data)
	{
		NodeFile *node = data;
		if(node->base.type == NodeTypeFile)
		{
			RELEASEIF(node->image);
		}
	}

	FREEIF(data);
}

static void LoadImageNotify(void *data, IImage * po, AEEImageInfo * pi, int nErr)
{
	ImageLoadData *loadData = data;
	FSModel *me = loadData->me;
	int size = IVECTORMODEL_Size(me->self);
	
	if(po && pi && nErr == SUCCESS)
	{
		loadData->node->imageInfo = *pi;
		loadData->node->loadStage = ImageLoadSuccess;
	}
	else
	{
		loadData->node->loadStage = ImageLoadFailed;
	}

	ListModel_Notify((IListModel*)me, EVT_MDL_LIST_CHANGE, loadData->node->base.index, size, size);
	FREEIF(loadData);
	CALLBACK_Resume(&me->imageCB, LoadImage, me, me->shell);
}

static void LoadImage(void *data)
{
	FSModel *me = data;
	ImageLoadData *loadData = MALLOCREC(ImageLoadData);
	NodeFile *node = NULL;

	IVECTORMODEL_GetAt(me->imagesToLoad, 0, (void**)&node);
	if(!node)
		return;
	IVECTORMODEL_DeleteAt(me->imagesToLoad, 0);

	node->image = ISHELL_LoadImage(me->shell, node->fi.szName);
	if(node->image && loadData){
		node->loadStage = ImageLoadInProgress;
		loadData->node = node;
		loadData->me = me;
		IIMAGE_Notify(node->image, LoadImageNotify, loadData);
	}else{
		node->loadStage = ImageLoadFailed;
		RELEASEIF(node->image);
		FREEIF(loadData);
		CALLBACK_Resume(&me->imageCB, LoadImage, me, me->shell);
	}
}

static void TraverseStep(void *data)
{
	FSModel *me = data;
	boolean enumRetval = FALSE;
	FileInfo fi;
	NodeFile *newNode = NULL;

	enumRetval = IFILEMGR_EnumNext(me->fmgr, &fi);
	if(!enumRetval){
		InitEnumDir(me);
		return;
	}

	newNode = MALLOCREC(NodeFile);
	if(newNode)
	{
		int index = IVECTORMODEL_Size(me->self);
		char *fname = NULL;
		newNode->base.type = NodeTypeFile;
		newNode->base.index = index;

		newNode->fi = fi;
		newNode->loadStage = ImageLoadPending;

		//init name
		fname = STRRCHR(fi.szName, '/');
		if(fname){
			AECHAR *leftDot = NULL;
			STRTOWSTR(fname+1, newNode->base.nodeName, sizeof(newNode->base.nodeName));
			if( leftDot = WSTRRCHR(newNode->base.nodeName, '.') )
				*leftDot = 0;
		}
		IVECTORMODEL_InsertAt(me->self, index, newNode);
	}

	CALLBACK_Resume(&me->traverseCB, TraverseStep, me, me->shell);
}

static void InitEnumDir(FSModel *me)
{
	char *path = NULL;
	int retval = EFAILED;

	if(IVECTORMODEL_Size(me->dirsToLoad) == 0)
	{
		ModelEvent event = {EVT_MDL_LOADED, 0};
		event.pModel = (IModel*)me;
		IModel_Notify(event.pModel, &event);
		return;
	}

	IVECTORMODEL_GetAt(me->dirsToLoad, 0, (void**)&path);
	retval = IFILEMGR_EnumInit(me->fmgr, path, FALSE);
	IVECTORMODEL_DeleteAt(me->dirsToLoad, 0);
	
	CALLBACK_Resume(&me->traverseCB, TraverseStep, me, me->shell);
}

uint32 FSModel_Release(IFSModel *intf)
{
	FSModel *me = (FSModel*)intf;
	uint32 refs = VectorModel_Release(me->self);
	
	if(refs == 0)
	{
		CALLBACK_Cancel(&me->imageCB);
		CALLBACK_Cancel(&me->traverseCB);
		RELEASEIF(me->dirsToLoad);
		RELEASEIF(me->imagesToLoad);
		RELEASEIF(me->fmgr);
		RELEASEIF(me->shell);
		FREEIF(me);
	}
	
	return SUCCESS;
}

void FSModel_SetPathList(IFSModel *intf, char **pathList)
{
	FSModel *me = (FSModel*)intf;
	int i;

	if(!pathList)
		return;

	for(i=0; pathList[i]!=NULL; i++)
	{
		char *path = STRDUP(pathList[i]);
		if(path == NULL)
			continue;
		IVECTORMODEL_Add(me->dirsToLoad, path);
	}

	InitEnumDir(me);
}

void FSModel_SetActionList(IFSModel *intf, uint16 *actionList, char *resFileName)
{
	FSModel *me = (FSModel*)intf;
	int i;

	if(!actionList)
		return;

	for(i=0; actionList[i] != 0; i++)
	{
		NodeAction *newAction = MALLOCREC(NodeAction);
		newAction->id = actionList[i];
		newAction->base.type = NodeTypeAction;
		newAction->base.index = i;
		ISHELL_LoadResString(me->shell, resFileName, newAction->id, 
			newAction->base.nodeName, sizeof(newAction->base.nodeName));
		IVECTORMODEL_InsertAt(me->self, i, newAction);
	}
}

void FSModel_LoadImages(IFSModel *intf, int startIndex, int count)
{
	FSModel *me = (FSModel*)intf;
	NodeFile *node = NULL;
	int i = startIndex;

	IVECTORMODEL_DeleteAll(me->imagesToLoad);
	while( i<startIndex+count && IVECTORMODEL_GetAt(me->self, i, (void**)&node) == SUCCESS)
	{
		if(node->base.type == NodeTypeFile && node->loadStage == ImageLoadPending)
			IVECTORMODEL_Add(me->imagesToLoad, node);
		i++;
	}
	CALLBACK_Resume(&me->imageCB, LoadImage, me, me->shell);
}

int FSModel_New(IFSModel **ppo, IShell *shell, IModule* module)
{
	int retval = SUCCESS;
	FSModel *me = MALLOCREC_VTBL(FSModel, IFSModel);

	if (!me) {
		return ENOMEMORY;
	}

	ISHELL_CreateInstance(shell, AEECLSID_VECTORMODEL, (void**)&me->dirsToLoad);
	ISHELL_CreateInstance(shell, AEECLSID_VECTORMODEL, (void**)&me->imagesToLoad);
	ISHELL_CreateInstance(shell, AEECLSID_FILEMGR, (void**)&me->fmgr);
	if(retval != SUCCESS)
	{
		FREEIF(me);
		return retval;
	}

	VectorModel_Ctor(&me->base, GETVTBL(me,IVectorModel), module);

	me->shell = shell;
	ADDREFIF(shell);
	GETVTBL(me,IFSModel)->SetPathList = FSModel_SetPathList;
	GETVTBL(me,IFSModel)->SetActionList = FSModel_SetActionList;
	GETVTBL(me,IFSModel)->LoadImages = FSModel_LoadImages;
	GETVTBL(me,IFSModel)->Release = FSModel_Release;

	me->self = (IVectorModel*)me;
	IVECTORMODEL_SetPfnFree(me->self, DestroyNode);
	IVECTORMODEL_SetPfnFree(me->dirsToLoad, FREE);

	*ppo = (IFSModel*)me;
		
	return SUCCESS;
}